package com.longtop.sp.mapdao.dialect.oracle;

import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.JdbcUtils;

import com.longtop.sp.mapdao.Lob;
import com.longtop.sp.mapdao.dialect.ConfigProperties;
import com.longtop.sp.mapdao.impl.AbstractUpdate;
import com.longtop.sp.mapdao.meta.IPrimaryKey;
import com.longtop.sp.mapdao.meta.TableMetaData;
import com.longtop.sp.mapdao.meta.TableMetaDataPool;

/**
 * 获取表的字段和Map数据的交集，以便准备sql语句。
 * 
 * @author 赵斌 2003-6-23
 * @author 胡长城 2003-6-23
 * @version $Revision: 1.1 $
 */

public class OracleUpdate extends AbstractUpdate {
	private static Log log = LogFactory.getLog(OracleUpdate.class);

	// 用于执行的sql语句，将它作为成员变量，便于在出错时打印在log日志中
	private String sql;

	/**
	 * @roseuid 3E8138E001A7
	 */
	public OracleUpdate() {
	}

	public int executeImpl() throws Exception {
		try {
			return executeImplWithException();
		} catch (SQLException ex) {
			log.warn("数据库组件Update在插入数据时发生SQLException异常！sql=" + sql);
			throw ex;
		} catch (IOException ex) {
			log.warn("数据库组件Update在插入数据时发生IOException异常！sql=" + sql);
			throw ex;
		}
	}

	public int executeImplWithException() throws Exception {
		// 获取主键
		IPrimaryKey pk = this.pkFactory.createPrimarykey(jdbcTemplate,
				tableName,ConfigProperties.getValue());
		TableMetaData rsmt = TableMetaDataPool.getMetaData(jdbcTemplate,
				tableName);
		String pkAndValue = pk.getPrimaryKeyAndValue(tmData, rsmt);

		// 获取表的字段和Map数据的交集，以便准备sql语句，也就是需要插入的数据
		FieldEntryList fields = new FieldEntryList(rsmt, tmData);

		// 没有发现可以插入的字段，不需要执行，抛出异常
		if (fields.getColumnCount() == 0) {
			throw new SQLException("Map中的数据与数据库表的字段名均不相同，没有发现可以插入的字段。");
		}
		sql = buildSql(fields, pkAndValue);
		return internalExecute(fields, pkAndValue);

	}

	private int internalExecute(FieldEntryList fields, String pkAndValue)
			throws Exception {
		Connection conn = DataSourceUtils.getConnection(jdbcTemplate
				.getDataSource());
		Statement stmt = null;
		ResultSet rs = null;
		int insertRowCount = 0;
		try {
			// 如果有lob字段，而且本来AutoCommit就是true，设为false
			boolean oldAutoCommit = conn.getAutoCommit();
			if (oldAutoCommit && fields.isHaveLob() && "oracle".equals(ConfigProperties.getValue())) {
				conn.setAutoCommit(false);
			}

			int updateRowCount = 0;
			stmt = conn.createStatement();
			updateRowCount = stmt.executeUpdate(sql);

			// 处理lob
			if (fields.isHaveLob() && "oracle".equals(ConfigProperties.getValue())) {
				// 将刚刚插入的纪录用 Select * from tab where ... for update定位
				StringBuffer sqlBuffer = new StringBuffer("SELECT ");
				boolean addComma = false;
				for (int i = 0, m = fields.getColumnCount(); i < m; i++) {
					if (fields.isLob(i)) {
						if (addComma) {
							sqlBuffer.append(",");
						}
						sqlBuffer.append(fields.getFieldName(i));
						addComma = true;
					}
				}
				sqlBuffer.append(" FROM " + tableName + " WHERE " + pkAndValue
						+ " FOR UPDATE");
				sql = new String(sqlBuffer);
				if (log.isDebugEnabled()) {
					log.debug("重新定位包含lob的纪录，准备处理lob字段sql=" + sql);
				}
				rs = stmt.executeQuery(sql);
				if (!rs.next()) {
					String msg = "未知错误，不能定位到刚刚插入或更新的纪录上。定位包含lob的纪录sql=" + sql;
					throw new SQLException(msg);
				}
				// 开始向LOB中写入数据
				Lob lob = new OracleLob();
				for (int i = 0; i < fields.getColumnCount(); i++) {
					if (fields.isBlob(i)) { // BLOB
						String f = fields.getFieldName(i);
						lob.writeBLOB(rs, f, tmData.get(f));
					} else if (fields.isClob(i)) { // CLOB
						String f = fields.getFieldName(i);
						lob.writeCLOB(rs, f, tmData.get(f));
					}
				}
			}

			// 处理提交问题，如果原本就是autoCommit，且因为有LOB而导致当前为不自动提交，就应该在这里提交
			if (oldAutoCommit && fields.isHaveLob()) {
				conn.commit();
			}

			return updateRowCount;
		} catch (Exception e) {
			throw e;
		} finally {
			JdbcUtils.closeResultSet(rs);
			JdbcUtils.closeStatement(stmt);

			DataSourceUtils.releaseConnection(conn, jdbcTemplate
					.getDataSource());
		}

	}

	private String buildSql(FieldEntryList fields, String pkAndValue) {
		// 构造一个update tableName set fields=value1, ... , where pkAndValue语句
		StringBuffer sqlBuffer = new StringBuffer();
		sqlBuffer.append("UPDATE " + tableName + " SET ");
		for (int i = 0; i < fields.getColumnCount(); i++) {
			if (i != 0) {
				sqlBuffer.append(",");
			}
			if(fields.getFieldValue(i).indexOf("POINT(") != -1 && "postgresql".equals(ConfigProperties.getValue())){
				sqlBuffer.append(fields.getFieldName(i) + "=(select st_geometryfromtext('" + fields.getFieldValue(i) + "',4326))");
			}else{
				if(fields.isHaveArray()){
					sqlBuffer.append(fields.getFieldName(i) + "= array["+fields.getFieldValue(i).substring(1, fields.getFieldValue(i).length()-1)+"]");
				}else{
					sqlBuffer.append(fields.getFieldName(i) + "=" + fields.getFieldValue(i));
				}
			}
		}
		sqlBuffer.append(" WHERE " + pkAndValue);

		sql = new String(sqlBuffer);
		if (log.isDebugEnabled()) {
			log.debug("更新的sql语句=" + sql);
		}
		return sql;
	}
}
